/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.view;

import java.awt.Component;
import java.awt.event.MouseListener;

import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.DocumentListener;
import javax.swing.text.Element;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import offset.nodes.client.editor.Editor;

public abstract class PopupTree implements DocumentListener {

    public class PopupNode extends DefaultMutableTreeNode {

        JPopupMenu popup = null;

        public PopupNode() {
            super();
        }

        public PopupNode(Object userObject) {
            super(userObject);
        }

        public String toString() {
            if (getUserObject() != null) {
                return getUserObject().toString();
            }
            
            return super.toString();
        }

        public JPopupMenu getPopup() {
            return popup;
        }

        public void setPopup(JPopupMenu popup) {
            this.popup = new JPopupMenu();

            JPopupMenu treePopup = PopupTree.this.getPopup();
            for (int i = 0; i < treePopup.getComponentCount(); i++) {
                Component c = treePopup.getComponent(i);
                if (c instanceof JMenuItem) {
                    JMenuItem oldItem = (JMenuItem) c;
                    JMenuItem newItem = new JMenuItem();
                    String text = oldItem.getText();
                    newItem.setAction(oldItem.getAction());
                    newItem.setText(text);

                    this.popup.add(newItem);
                    if (popup.getComponentCount() > 0) {
                        this.popup.addSeparator();
                    }
                }
            }

            for (int i = 0; i < popup.getComponentCount(); i++) {
                Component c = popup.getComponent(i);
                if (c instanceof JMenuItem) {
                    JMenuItem oldItem = (JMenuItem) c;
                    JMenuItem newItem = new JMenuItem();
                    String text = oldItem.getText();
                    newItem.setAction(oldItem.getAction());
                    newItem.setText(text);

                    this.popup.add(newItem);
                } else if (c instanceof JPopupMenu.Separator) {
                    this.popup.addSeparator();
                }
            }
        }
    }
    protected JTree tree;
    protected JPopupMenu popup;
    protected NodePopup[] nodePopups = null;
    protected MouseListener[] defaultMouseListeners;

    public PopupTree(JTree tree, Editor editor) {
        super();
        this.tree = tree;
        this.popup = createPopupMenu(editor);

        /* DOES NOT WORK IN JSE 6. 
        defaultMouseListeners = tree.getMouseListeners();
        for( int i=0; i<defaultMouseListeners.length; i++)
        tree.removeMouseListener(defaultMouseListeners[i]);	*/
        tree.addMouseListener(new java.awt.event.MouseAdapter() {

            public void mouseReleased(java.awt.event.MouseEvent evt) {
                PopupTree.this.mouseReleased(evt);
            }
        });
    }

    public abstract DefaultMutableTreeNode createTree();

    protected String getName(DefaultMutableTreeNode node) {
        if (node.getUserObject() instanceof Element) {
            return ((Element) node.getUserObject()).getName();
        }

        return "";
    }

    protected DefaultMutableTreeNode findNode(DefaultMutableTreeNode root, Element element) {
        if (root.getUserObject() == element) {
            return root;
        }

        for (int i = 0; i < root.getChildCount(); i++) {
            DefaultMutableTreeNode node = findNode((DefaultMutableTreeNode) root.getChildAt(i), element);
            if (node != null) {
                return node;
            }
        }

        return null;
    }

    public DefaultMutableTreeNode findNode(Element e) {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) ((DefaultTreeModel) tree.getModel()).getRoot();
        return findNode(root, e);
    }

    protected abstract JPopupMenu createPopupMenu(Editor editor);

    protected abstract void defaultAction();

    public PopupNode createPopupNode(Object userObject) {
        PopupNode node = new PopupNode(userObject);
        setNodePopup(node);

        return node;
    }

    private void mouseReleased(java.awt.event.MouseEvent evt) {
        java.awt.Component c = evt.getComponent();
        if (c != null) {
            refresh();
            
            // Show popup menu for right click
            JPopupMenu popup = getPopup();
            if (tree.getSelectionCount() <= 1 && javax.swing.SwingUtilities.isRightMouseButton(evt)) {
                if (tree.getPathForLocation(evt.getX(), evt.getY()) != null) {
                    tree.setSelectionPath(tree.getPathForLocation(evt.getX(), evt.getY()));
                }
            }

            if (tree.getSelectionCount() > 0) {
                PopupNode node = (PopupNode) tree.getSelectionPath().getLastPathComponent();
                if (node.getPopup() != null) {
                    popup = node.getPopup();
                }
            }


            if (javax.swing.SwingUtilities.isRightMouseButton(evt)) {
                popup.show(c, evt.getX(), evt.getY());
                return;
            } else if (evt.getClickCount() >= 2) {
                defaultAction();
                return;
            }
        }

    /*for( int i=0; i<defaultMouseListeners.length; i++)
    defaultMouseListeners[i].mouseReleased(evt);*/

    }

    public JPopupMenu getPopup() {
        return popup;
    }

    public void setPopup(JPopupMenu popup) {
        this.popup = popup;
    }

    public interface NodePopup {

        public boolean isPopupNode(PopupNode node);

        public JPopupMenu getPopup();
    }

    public interface PopupItem {

        public JMenuItem getItem();

        public boolean isItemNode(PopupNode node);
    }

    public static class ItemGroup {

        PopupItem[] items;

        public ItemGroup(PopupItem[] items) {
            this.items = items;
        }

        public PopupItem[] getItems() {
            return items;
        }
    }

    public static abstract class AbstractPopupItem implements PopupItem {

        Editor editor;
        Action action;

        public AbstractPopupItem(Editor editor, Action action) {
            this.editor = editor;
            this.action = action;
        }

        public JMenuItem getItem() {
            JMenuItem item = new JMenuItem();
            item.setAction(action);

            return item;
        }

        public Editor getEditor() {
            return editor;
        }

        public Action getAction() {
            return action;
        }
    }

    public static class UnconditionalPopupItem extends AbstractPopupItem {

        public UnconditionalPopupItem(Editor editor, Action action) {
            super(editor, action);
        }

        public boolean isItemNode(PopupNode node) {
            return true;
        }
    }

    public static class ItemGroupsPopup implements NodePopup {

        ItemGroup[] groups;
        PopupNode node = null;
        JPopupMenu menu = null;

        public ItemGroupsPopup(ItemGroup[] groups) {
            this.groups = groups;
        }

        public JPopupMenu getPopup() {
            JPopupMenu m = menu;
            menu = null;
            return m;
        }

        public boolean isPopupNode(PopupNode node) {
            int lastGroup = -1;

            for (int i = 0; i < groups.length; i++) {
                for (int j = 0; j < groups[i].getItems().length; j++) {
                    if (groups[i].getItems()[j].isItemNode(node)) {
                        if (menu == null) {
                            menu = new JPopupMenu();
                        }
                        if (lastGroup >= 0 && lastGroup != i) {
                            menu.addSeparator();
                        }
                        lastGroup = i;
                        menu.add(groups[i].getItems()[j].getItem());
                    }
                }
            }

            return menu != null;
        }
        
        public void addItemGroupsPopup(ItemGroupsPopup addItemGroupsPopup) {
            ItemGroup[] g = new ItemGroup[getGroups().length+addItemGroupsPopup.getGroups().length];
            int i=0;
            for( ; i<getGroups().length; i++) {
                g[i] = getGroups()[i];
            }
            
            for( int j=0; j<addItemGroupsPopup.getGroups().length; j++) {
                g[i+j] = addItemGroupsPopup.getGroups()[j];
            }
            
            setGroups(g);
        }

        public ItemGroup[] getGroups() {
            return groups;
        }

        public void setGroups(ItemGroup[] groups) {
            this.groups = groups;
        }
    }

    protected void setNodePopups(PopupNode node, NodePopup[] nodePopups) {
        for (int i = 0; i < nodePopups.length; i++) {
            if (nodePopups[i].isPopupNode(node)) {
                node.setPopup(nodePopups[i].getPopup());
            }
        }

        for (int i = 0; i < node.getChildCount(); i++) {
            setNodePopups((PopupNode) node.getChildAt(i), nodePopups);
        }
    }

    public void refresh() {
        if (nodePopups != null) {
            setNodePopups(nodePopups);
        }
    }

    public NodePopup[] getNodePopups() {
        return nodePopups;
    }
    
   List<NodePopup> popups = new ArrayList<NodePopup>();
   
   public void addItemGroupsPopup(ItemGroupsPopup addItemGroupsPopup) {
       for( int i=0; i < popups.size(); i++ ) {
           if ( popups.get(i) instanceof ItemGroupsPopup) {
               ((ItemGroupsPopup)popups.get(i)).addItemGroupsPopup(addItemGroupsPopup);
               return;
           }
       }
       
       // if not found, add it as the first item groups popup
       popups.add(addItemGroupsPopup);
   }

    public void addNodePopups(NodePopup[] add) {
        for (int i = 0; i < add.length; i++) {
            if ( add[i] instanceof ItemGroupsPopup)
                addItemGroupsPopup((ItemGroupsPopup)add[i]);
            else
                popups.add(add[i]);
        }
        
        setNodePopups(popups.toArray(new NodePopup[0]));
    }

    public void setNodePopups(NodePopup[] nodePopups) {
        if (nodePopups == null) {
            return;
        }
        this.nodePopups = nodePopups;
        setNodePopups((PopupNode) ((DefaultTreeModel) tree.getModel()).getRoot(), nodePopups);
    }

    public void setNodePopup(PopupNode node) {
        if (nodePopups == null) {
            return;
        }

        for (int i = 0; i < nodePopups.length; i++) {
            if (nodePopups[i].isPopupNode(node)) {
                node.setPopup(nodePopups[i].getPopup());
            }
        }

    }
}
